#define __LIBRARY__
#include <linux/sem.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

_syscall2(sem_t *,sem_open,const char *,name,unsigned int,value)
_syscall1(int,sem_wait,sem_t *,sem)
_syscall1(int,sem_post,sem_t *,sem)
_syscall1(int,sem_unlink,const char *,name)

#define BUFSIZE 10
#define CON_NUM 5 
#define PRODUCTS 500
const char * file_name = "./lab6buffer";
const char * full_name = "lab6full";
const char * empty_name = "lab6empty";
const char * mutex_name = "lab6mutex";
sem_t *empty, *full, *mutex;

void Producer()
{
    int fd, num = 0;
    while (num <= PRODUCTS)
    {
        sem_wait(empty);
        sem_wait(mutex);
        fd = open(file_name, O_WRONLY | O_APPEND);
        write(fd, &num, sizeof(int));
        close(fd);
        sem_post(mutex);
        sem_post(full);
        num++;
    }

    return;
}

void Consumer()
{
    int fd, ret;
    int buf[BUFSIZE];
    while (1)
    {
        sem_wait(full);
        sem_wait(mutex);
    
        fd = open(file_name, O_RDONLY);
        ret = read(fd, buf, BUFSIZE * sizeof(int)) / (sizeof(int));
        close(fd);
        if (ret)
        {
            printf("%d: %d\n", getpid(), buf[0]);
            fflush(stdout);
        }
        
        fd = open(file_name, O_WRONLY | O_TRUNC);
        if (ret > 1)
            write(fd, &buf[1], (ret - 1) * sizeof(int)); 
        close(fd);
        sem_post(mutex);
        sem_post(empty);
    }
    return;
}

int main()
{
    int fd, i;
    pid_t pid;
    empty = sem_open(empty_name, BUFSIZE); 
    full = sem_open(full_name, 0);         
    mutex = sem_open(mutex_name, 1);       

    
    fd = open(file_name, O_CREAT | O_RDWR | O_TRUNC, 0666);
    close(fd);
    
    if (!(pid = fork()))
    {
        Producer();
        return 0;
    }
    
    for (i = 0; i < CON_NUM; i++)
    {
        if (!fork())
        {
            Consumer();
            return 0;
        }
    }
    
    waitpid(pid, NULL, 0);
    sleep(2);         
    kill(0, SIGKILL); 
    
    sem_unlink(full_name);
    sem_unlink(empty_name);
    sem_unlink(mutex_name);
    return 0;
}
